package com.gitee.zycra.future.job.common.util;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.File;
import java.io.FileInputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.MessageDigest;

/**
 * @author zycra
 */
@Slf4j
public final class OneWayEncryptUtil extends BCUtil {

    private static final String MD5_LABEL = "MD5";
    private static final String SHA1_LABEL = "SHA1";
    private static final String SHA256_LABEL = "SHA-256";
    private static final String SM3_LABEL = "SM3";
    private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
    private static final int TWO = 2;
    private static final int FOUR = 4;
    private static final int SIXTEEN = 16;
    private static final int F = 0x0F;

    private OneWayEncryptUtil() {
    }

    /**
     * MD5加密
     *
     * @param content 待加密内容
     * @return 加密后字符串
     */
    public static String md5(String content) {
        return oneWayEncrypt(MD5_LABEL, content);
    }

    /**
     * MD5加密文件
     *
     * @param file 待加密文件
     * @return 加密后字符串
     */
    public static String md5(File file) {
        return oneWayEncrypt(MD5_LABEL, file);
    }

    /**
     * SHA1加密
     *
     * @param content 待加密内容
     * @return 加密后字符串
     */
    public static String sha1(String content) {
        return oneWayEncrypt(SHA1_LABEL, content);
    }

    /**
     * SHA1加密文件
     *
     * @param file 待加密文件
     * @return 加密后字符串
     */
    public static String sha1(File file) {
        return oneWayEncrypt(SHA1_LABEL, file);
    }

    /**
     * SHA256加密
     *
     * @param content 待加密内容
     * @return 加密后字符串
     */
    public static String sha256(String content) {
        return oneWayEncrypt(SHA256_LABEL, content);
    }

    /**
     * SHA256加密文件
     *
     * @param file 待加密文件
     * @return 加密后字符串
     */
    public static String sha256(File file) {
        return oneWayEncrypt(SHA256_LABEL, file);
    }

    /**
     * sm3加密
     *
     * @param content 待加密内容
     * @return 加密后字符串
     */
    public static String sm3(String content) {
        return oneWayEncrypt(SM3_LABEL, content);
    }

    /**
     * sm3加密文件
     *
     * @param file 待加密文件
     * @return 加密后字符串
     */
    public static String sm3(File file) {
        return oneWayEncrypt(SM3_LABEL, file);
    }

    /**
     * byte数组转十六进制字符串
     *
     * @param bytes byte数组
     * @return 十六进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        byte[] hexChars = new byte[bytes.length << 1];
        int index = 0;
        for (int v : bytes) {
            hexChars[index++] = HEX_ARRAY[v >>> FOUR & F];
            hexChars[index++] = HEX_ARRAY[v & F];
        }
        return new String(hexChars, StandardCharsets.UTF_8);
    }

    /**
     * 十六进制字符串转byte数组
     *
     * @param hex 十六进制字符串
     * @return byte数组
     */
    public static byte[] hexToBytes(String hex) {
        int byteLength = hex.length() >> 1;
        byte[] result = new byte[byteLength];
        for (int i = 0; i < byteLength; i++) {
            int offset = i << 1;
            result[i] = (byte) Integer.parseInt(hex.substring(offset, offset + TWO), SIXTEEN);
        }
        return result;
    }

    private static String oneWayEncrypt(String type, String content) {
        if (content == null) {
            return null;
        }
        try {
            MessageDigest digest = MessageDigest.getInstance(type, BouncyCastleProvider.PROVIDER_NAME);
            digest.update(content.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest.digest());
        } catch (Exception e) {
            log.error("oneWayEncrypt error", e);
        }
        return null;
    }

    private static String oneWayEncrypt(String type, File file) {
        if (!Files.isReadable(file.toPath())) {
            return null;
        }
        try (FileInputStream in = new FileInputStream(file);
             FileChannel ch = in.getChannel()) {
            MessageDigest digest = MessageDigest.getInstance(type, BouncyCastleProvider.PROVIDER_NAME);
            MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
            digest.update(byteBuffer);
            return bytesToHex(digest.digest());
        } catch (Exception e) {
            log.error("oneWayEncrypt file error", e);
        }
        return null;
    }
}
